<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.3.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/yuwanzi.io/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/yuwanzi.io/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/yuwanzi.io/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/yuwanzi.io/images/logo.svg" color="#222">

<link rel="stylesheet" href="/yuwanzi.io/css/main.css">



<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"suyuhuan.gitee.io","root":"/yuwanzi.io/","images":"/yuwanzi.io/images","scheme":"Muse","version":"8.2.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Suche...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}};
  </script>
<meta name="description" content="什么是注解? 注解是JDK1.5引入的一个语法糖，它主要用来当作元数据，简单的说就是用于解释数据的数据。在Java中，类、方法、变量、参数、包都可以被注解。很多开源框架都使用了注解，例如Spring、MyBatis、Junit。我们平常最常见的注解可能就是@Override了，该注解用来标识一个重写的函数。 注解的作用：  配置文件：替代xml等文本文件格式的配置文件。使用注解作为配置文件可以在代">
<meta property="og:type" content="article">
<meta property="og:title" content="注解的那点事儿">
<meta property="og:url" content="https://suyuhuan.gitee.io/yuwanzi.io/2017/10/15/2017-10-15-JavaAnnotation/index.html">
<meta property="og:site_name" content="玉丸子 | Blog">
<meta property="og:description" content="什么是注解? 注解是JDK1.5引入的一个语法糖，它主要用来当作元数据，简单的说就是用于解释数据的数据。在Java中，类、方法、变量、参数、包都可以被注解。很多开源框架都使用了注解，例如Spring、MyBatis、Junit。我们平常最常见的注解可能就是@Override了，该注解用来标识一个重写的函数。 注解的作用：  配置文件：替代xml等文本文件格式的配置文件。使用注解作为配置文件可以在代">
<meta property="og:locale">
<meta property="article:published_time" content="2017-10-15T10:00:00.000Z">
<meta property="article:modified_time" content="2020-11-07T00:58:17.000Z">
<meta property="article:author" content="玉丸子">
<meta property="article:tag" content="Java">
<meta property="article:tag" content="2017">
<meta property="article:tag" content="后端">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/10/15/2017-10-15-JavaAnnotation/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh'
  };
</script>
<title>注解的那点事儿 | 玉丸子 | Blog</title>
  




  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

<link rel="alternate" href="/yuwanzi.io/atom.xml" title="玉丸子 | Blog" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Navigationsleiste an/ausschalten" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/yuwanzi.io/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">玉丸子 | Blog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Inhaltsverzeichnis
        </li>
        <li class="sidebar-nav-overview">
          Übersicht
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E6%B3%A8%E8%A7%A3"><span class="nav-number">1.</span> <span class="nav-text">什么是注解?</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%A8%E8%A7%A3%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="nav-number">2.</span> <span class="nav-text">注解的实现</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%9E%E8%B7%B5-%E5%8C%85%E6%89%AB%E6%8F%8F%E5%99%A8"><span class="nav-number">3.</span> <span class="nav-text">实践-包扫描器</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">玉丸子</p>
  <div class="site-description" itemprop="description">这里是玉丸子的个人博客,与你一起发现更大的世界。</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/yuwanzi.io/archives">
          <span class="site-state-item-count">68</span>
          <span class="site-state-item-name">Artikel</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/yuwanzi.io/categories/">
        <span class="site-state-item-count">39</span>
        <span class="site-state-item-name">Kategorien</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/yuwanzi.io/tags/">
        <span class="site-state-item-count">46</span>
        <span class="site-state-item-name">schlagwörter</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh">
    <link itemprop="mainEntityOfPage" href="https://suyuhuan.gitee.io/yuwanzi.io/2017/10/15/2017-10-15-JavaAnnotation/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/yuwanzi.io/images/avatar.gif">
      <meta itemprop="name" content="玉丸子">
      <meta itemprop="description" content="这里是玉丸子的个人博客,与你一起发现更大的世界。">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="玉丸子 | Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          注解的那点事儿
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Veröffentlicht am</span>

      <time title="Erstellt: 2017-10-15 18:00:00" itemprop="dateCreated datePublished" datetime="2017-10-15T18:00:00+08:00">2017-10-15</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">Bearbeitet am</span>
        <time title="Geändert am: 2020-11-07 08:58:17" itemprop="dateModified" datetime="2020-11-07T08:58:17+08:00">2020-11-07</time>
      </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">in</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">后端</span></a>
        </span>
          . 
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/yuwanzi.io/categories/%E5%90%8E%E7%AB%AF/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h3 id="什么是注解"><a href="#什么是注解" class="headerlink" title="什么是注解?"></a>什么是注解?</h3><hr>
<p><strong>注解是<code>JDK1.5</code>引入的一个语法糖，它主要用来当作元数据，简单的说就是用于解释数据的数据</strong>。在Java中，类、方法、变量、参数、包都可以被注解。很多开源框架都使用了注解，例如<code>Spring</code>、<code>MyBatis</code>、<code>Junit</code>。我们平常最常见的注解可能就是<code>@Override</code>了，该注解用来标识一个重写的函数。</p>
<p>注解的作用：</p>
<ul>
<li><p>配置文件：替代<code>xml</code>等文本文件格式的配置文件。使用注解作为配置文件可以在代码中实现动态配置，相比外部配置文件，注解的方式会减少很多文本量。但缺点也很明显，更改配置需要对代码进行重新编译，无法像外部配置文件一样进行集中管理（所以现在基本都是外部配置文件+注解混合使用）。</p>
</li>
<li><p>数据的标记：注解可以作为一个标记（例如：被<code>@Override</code>标记的方法代表被重写的方法）。</p>
</li>
<li><p>减少重复代码：注解可以减少重复且乏味的代码。比如我们定义一个<code>@ValidateInt</code>，然后通过反射来获得类中所有成员变量，只要是含有<code>@ValidateInt</code>注解的成员变量，我们就可以对其进行数据的规则校验。</p>
</li>
</ul>
<p>定义一个注解非常简单，只需要遵循以下的语法规则：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(ElementType.FIELD)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> ValidateInt &#123;</span><br><span class="line">	<span class="comment">// 它们看起来像是定义一个函数，但其实这是注解中的属性</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">maxLength</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">minLength</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们发现上面的代码在定义注解时也使用了注解，这些注解被称为元注解。<strong>作用于注解上的注解称为元注解（元注解其实就是注解的元数据）</strong>，<code>Java</code>中一共有以下元注解。</p>
<ul>
<li><p><code>@Target</code>：用于描述注解的使用范围（注解可以用在什么地方）。</p>
<ul>
<li><p>ElementType.CONSTRUCTOR：构造器。</p>
</li>
<li><p>ElementType.FIELD：成员变量。</p>
</li>
<li><p>ElementType.LOCAL_VARIABLE：局部变量。</p>
</li>
<li><p>ElementType.PACKAGE：包。</p>
</li>
<li><p>ElementType.PARAMETER：参数。</p>
</li>
<li><p>ElementType.METHOD：方法。</p>
</li>
<li><p>ElementType.TYPE：类、接口(包括注解类型) 或enum声明。</p>
</li>
</ul>
</li>
<li><p><code>@Retention</code>：注解的生命周期，用于表示该注解会在什么时期保留。</p>
<ul>
<li><p>RetentionPolicy.RUNTIME：运行时保留，这样就可以通过反射获得了。</p>
</li>
<li><p>RetentionPolicy.CLASS：在class文件中保留。</p>
</li>
<li><p>RetentionPolicy.SOURCE：在源文件中保留。</p>
</li>
</ul>
</li>
<li><p><code>@Documented</code>：表示该注解会被作为被标注的程序成员的公共API，因此可以被例如javadoc此类的工具文档化。</p>
</li>
<li><p><code>@Inherited</code>：表示该注解是可被继承的（如果一个使用了@Inherited修饰的annotation类型被用于一个class，则这个annotation将被用于该class的子类）。</p>
</li>
</ul>
<p>了解了这些基础知识之后，接着完成上述定义的<code>@ValidateInt</code>，我们定义一个<code>Cat</code>类然后在它的成员变量中使用<code>@ValidateInt</code>，并通过反射进行数据校验。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Cat</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ValidateInt(minLength = 0, maxLength = 10)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getAge</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAge</span><span class="params">(<span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.age = age;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IllegalAccessException </span>&#123;</span><br><span class="line">        Cat cat = <span class="keyword">new</span> Cat();</span><br><span class="line">        cat.setName(<span class="string">&quot;楼楼&quot;</span>);</span><br><span class="line">        cat.setAge(<span class="number">11</span>);</span><br><span class="line"></span><br><span class="line">        Class&lt;? extends Cat&gt; clazz = cat.getClass();</span><br><span class="line">        Field[] fields = clazz.getDeclaredFields();</span><br><span class="line">        <span class="keyword">if</span> (fields != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Field field : fields) &#123;</span><br><span class="line">                ValidateInt annotation = field.getDeclaredAnnotation(ValidateInt.class);</span><br><span class="line">                <span class="keyword">if</span> (annotation != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    field.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">                    <span class="keyword">int</span> value = field.getInt(cat);</span><br><span class="line">                    <span class="keyword">if</span> (value &lt; annotation.minLength()) &#123;</span><br><span class="line">                        <span class="comment">// ....</span></span><br><span class="line">                    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (value &gt; annotation.maxLength()) &#123;</span><br><span class="line">                        <span class="comment">// ....</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>本文作者为:<a target="_blank" rel="noopener" href="https://github.com/SylvanasSun">SylvanasSun(sylvanas.sun@gmail.com)</a>，首发于<a target="_blank" rel="noopener" href="https://sylvanassun.github.io/">SylvanasSun’s Blog</a>。<br>原文链接：<a target="_blank" rel="noopener" href="https://sylvanassun.github.io/2017/10/15/2017-10-15-JavaAnnotation/">https://sylvanassun.github.io/2017/10/15/2017-10-15-JavaAnnotation/</a><br>（转载请务必保留本段声明，并且保留超链接。）</p>
</blockquote>
<h3 id="注解的实现"><a href="#注解的实现" class="headerlink" title="注解的实现"></a>注解的实现</h3><hr>
<p>注解其实只是<code>Java</code>的一颗语法糖（语法糖是一种方便程序员使用的语法规则，但它其实并没有表面上那么神奇的功能，只不过是由编译器帮程序员生成那些繁琐的代码）。在<code>Java</code>中这样的语法糖还有很多，例如<code>enum</code>、泛型、<code>forEach</code>等。</p>
<p>通过阅读<a target="_blank" rel="noopener" href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6">JLS(Java Language Specification</a>（当你想了解一个语言特性的实现时，最好的方法就是阅读官方规范）发现，<strong>注解是一个继承自<code>java.lang.annotation.Annotation</code>接口的特殊接口</strong>，原文如下：</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">An annotation type declaration specifies a new annotation type, a special kind of interface type. To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (@).</span><br><span class="line"></span><br><span class="line">Note that the at-sign (@) and the keyword interface are distinct tokens. It is possible to separate them with whitespace, but this is discouraged as a matter of style.</span><br><span class="line"></span><br><span class="line">The rules for annotation modifiers on an annotation type declaration are specified in §9.7.4 and §9.7.5.</span><br><span class="line"></span><br><span class="line">The Identifier in an annotation type declaration specifies the name of the annotation type.</span><br><span class="line"></span><br><span class="line">It is a compile-time error if an annotation type has the same simple name as any of its enclosing classes or interfaces.</span><br><span class="line"></span><br><span class="line">The direct superinterface of every annotation type is java.lang.annotation.Annotation.</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.lang.annotation;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The common interface extended by all annotation types.  Note that an</span></span><br><span class="line"><span class="comment"> * interface that manually extends this one does &lt;i&gt;not&lt;/i&gt; define</span></span><br><span class="line"><span class="comment"> * an annotation type.  Also note that this interface does not itself</span></span><br><span class="line"><span class="comment"> * define an annotation type.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * More information about annotation types can be found in section 9.6 of</span></span><br><span class="line"><span class="comment"> * &lt;cite&gt;The Java&amp;trade; Language Specification&lt;/cite&gt;.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * The &#123;<span class="doctag">@link</span> java.lang.reflect.AnnotatedElement&#125; interface discusses</span></span><br><span class="line"><span class="comment"> * compatibility concerns when evolving an annotation type from being</span></span><br><span class="line"><span class="comment"> * non-repeatable to being repeatable.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>  Josh Bloch</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span>   1.5</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Annotation</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们将上节定义的<code>@ValidateInt</code>注解进行反编译来验证这个说法。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">  Last modified Oct <span class="number">14</span>, <span class="number">2017</span>; size <span class="number">479</span> bytes</span><br><span class="line">  MD5 checksum 2d9dd2c169fe854db608c7950af3eca7</span><br><span class="line">  Compiled from <span class="string">&quot;ValidateInt.java&quot;</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">com</span>.<span class="title">sun</span>.<span class="title">annotation</span>.<span class="title">ValidateInt</span> <span class="keyword">extends</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">annotation</span>.<span class="title">Annotation</span></span></span><br><span class="line">  minor version: 0</span><br><span class="line">  major version: <span class="number">52</span></span><br><span class="line">  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION</span><br><span class="line">Constant pool:</span><br><span class="line">   #1 = Class              #18            // com/sun/annotation/ValidateInt</span><br><span class="line">   #2 = Class              #19            // java/lang/Object</span><br><span class="line">   #3 = Class              #20            // java/lang/annotation/Annotation</span><br><span class="line">   #4 = Utf8               maxLength</span><br><span class="line">   #5 = Utf8               ()I</span><br><span class="line">   #6 = Utf8               minLength</span><br><span class="line">   #7 = Utf8               SourceFile</span><br><span class="line">   #8 = Utf8               ValidateInt.java</span><br><span class="line">   #9 = Utf8               RuntimeVisibleAnnotations</span><br><span class="line">  #10 = Utf8               Ljava/lang/annotation/Retention;</span><br><span class="line">  #11 = Utf8               value</span><br><span class="line">  #12 = Utf8               Ljava/lang/annotation/RetentionPolicy;</span><br><span class="line">  #13 = Utf8               RUNTIME</span><br><span class="line">  #14 = Utf8               Ljava/lang/annotation/Target;</span><br><span class="line">  #15 = Utf8               Ljava/lang/annotation/ElementType;</span><br><span class="line">  #16 = Utf8               FIELD</span><br><span class="line">  #17 = Utf8               Ljava/lang/annotation/Documented;</span><br><span class="line">  #18 = Utf8               com/sun/annotation/ValidateInt</span><br><span class="line">  #19 = Utf8               java/lang/Object</span><br><span class="line">  #20 = Utf8               java/lang/annotation/Annotation</span><br><span class="line">&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">maxLength</span><span class="params">()</span></span>;</span><br><span class="line">    descriptor: ()I</span><br><span class="line">    flags: ACC_PUBLIC, ACC_ABSTRACT</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">minLength</span><span class="params">()</span></span>;</span><br><span class="line">    descriptor: ()I</span><br><span class="line">    flags: ACC_PUBLIC, ACC_ABSTRACT</span><br><span class="line">&#125;</span><br><span class="line">SourceFile: <span class="string">&quot;ValidateInt.java&quot;</span></span><br><span class="line">RuntimeVisibleAnnotations:</span><br><span class="line">  0: #10(#11=e#12.#13)</span><br><span class="line">  1: #14(#11=[e#15.#16])</span><br><span class="line">  2: #17()</span><br></pre></td></tr></table></figure>
<p><code>public interface com.sun.annotation.ValidateInt extends java.lang.annotation.Annotation</code>，很明显<code>ValidateInt</code>继承自<code>java.lang.annotation.Annotation</code>。</p>
<p>那么，如果注解只是一个接口，又是如何实现对属性的设置呢？这是<strong>因为<code>Java</code>使用了动态代理对我们定义的注解接口生成了一个代理类，而对注解的属性设置其实都是在对这个代理类中的变量进行赋值</strong>。所以我们才能用反射获得注解中的各种属性。</p>
<p>为了证实注解其实是个动态代理对象，接下来我们使用<code>CLHSDB(Command-Line HotSpot Debugger)</code>来查看<code>JVM</code>的运行时数据。如果有童鞋不了解怎么使用的话，可以参考R大的文章<a target="_blank" rel="noopener" href="http://rednaxelafx.iteye.com/blog/1847971">借HSDB来探索HotSpot VM的运行时数据 - Script Ahead, Code Behind - ITeye博客</a>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0x000000000257f538</span> com/sun/proxy/$Proxy1</span><br></pre></td></tr></table></figure>
<p>注解的类型为<code>com/sun/proxy/$Proxy1</code>，这正是动态代理生成代理类的默认类型，<code>com/sun/proxy</code>为默认包名，<code>$Proxy</code>是默认的类名，<code>1</code>为自增的编号。</p>
<h3 id="实践-包扫描器"><a href="#实践-包扫描器" class="headerlink" title="实践-包扫描器"></a>实践-包扫描器</h3><hr>
<p>我们在使用<code>Spring</code>的时候，只需要指定一个包名，框架就会去扫描该包下所有带有<code>Spring</code>中的注解的类。实现一个包扫描器很简单，主要思路如下：</p>
<ul>
<li><p>先将传入的包名通过类加载器获得项目内的路径。</p>
</li>
<li><p>然后遍历并获得该路径下的所有class文件路径（需要处理为包名的格式）。</p>
</li>
<li><p>得到了class文件的路径就可以使用反射生成Class对象并获得其中的各种信息了。</p>
</li>
</ul>
<p>定义包扫描器接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">PackageScanner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    List&lt;Class&lt;?&gt;&gt; scan(String packageName);</span><br><span class="line"></span><br><span class="line">    List&lt;Class&lt;?&gt;&gt; scan(String packageName, ScannedClassHandler handler);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>函数2需要传入一个<code>ScannedClassHandler</code>接口，该接口是我们定义的回调函数，用于在扫描所有类文件之后执行的处理操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span> <span class="comment">// 这个注解表示该接口为一个函数接口，用于支持Lambda表达式</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ScannedClassHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">execute</span><span class="params">(Class&lt;?&gt; clazz)</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我想要包扫描器可以识别和支持不同的文件类型，定义一个枚举类<code>ResourceType</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">enum</span> <span class="title">ResourceType</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    JAR(<span class="string">&quot;jar&quot;</span>),</span><br><span class="line">    FILE(<span class="string">&quot;file&quot;</span>),</span><br><span class="line">    CLASS_FILE(<span class="string">&quot;class&quot;</span>),</span><br><span class="line">    INVALID(<span class="string">&quot;invalid&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String typeName;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getTypeName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.typeName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">ResourceType</span><span class="params">(String typeName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.typeName = typeName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>PathUtils</code>是一个用来处理路径和包转换等操作的工具类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PathUtils</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String FILE_SEPARATOR = System.getProperty(<span class="string">&quot;file.separator&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String CLASS_FILE_SUFFIX = <span class="string">&quot;.class&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String JAR_PROTOCOL = <span class="string">&quot;jar&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String FILE_PROTOCOL = <span class="string">&quot;file&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">PathUtils</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 去除后缀名</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">trimSuffix</span><span class="params">(String filename)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (filename == <span class="keyword">null</span> || <span class="string">&quot;&quot;</span>.equals(filename))</span><br><span class="line">            <span class="keyword">return</span> filename;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> dotIndex = filename.lastIndexOf(<span class="string">&quot;.&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (-<span class="number">1</span> == dotIndex)</span><br><span class="line">            <span class="keyword">return</span> filename;</span><br><span class="line">        <span class="keyword">return</span> filename.substring(<span class="number">0</span>, dotIndex);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">pathToPackage</span><span class="params">(String path)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (path == <span class="keyword">null</span> || <span class="string">&quot;&quot;</span>.equals(path))</span><br><span class="line">            <span class="keyword">return</span> path;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (path.startsWith(FILE_SEPARATOR))</span><br><span class="line">            path = path.substring(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> path.replace(FILE_SEPARATOR, <span class="string">&quot;.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">packageToPath</span><span class="params">(String packageName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (packageName == <span class="keyword">null</span> || <span class="string">&quot;&quot;</span>.equals(packageName))</span><br><span class="line">            <span class="keyword">return</span> packageName;</span><br><span class="line">        <span class="keyword">return</span> packageName.replace(<span class="string">&quot;.&quot;</span>, FILE_SEPARATOR);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据URL的协议来判断资源类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ResourceType <span class="title">getResourceType</span><span class="params">(URL url)</span> </span>&#123;</span><br><span class="line">        String protocol = url.getProtocol();</span><br><span class="line">        <span class="keyword">switch</span> (protocol) &#123;</span><br><span class="line">            <span class="keyword">case</span> JAR_PROTOCOL:</span><br><span class="line">                <span class="keyword">return</span> ResourceType.JAR;</span><br><span class="line">            <span class="keyword">case</span> FILE_PROTOCOL:</span><br><span class="line">                <span class="keyword">return</span> ResourceType.FILE;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> ResourceType.INVALID;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">isClassFile</span><span class="params">(String path)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (path == <span class="keyword">null</span> || <span class="string">&quot;&quot;</span>.equals(path))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">return</span> path.endsWith(CLASS_FILE_SUFFIX);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 抽取URL中的主要路径.</span></span><br><span class="line"><span class="comment">     * Example:</span></span><br><span class="line"><span class="comment">     * &quot;file:/com/example/hello&quot; to &quot;/com/example/hello&quot;</span></span><br><span class="line"><span class="comment">     * &quot;jar:file:/com/example/hello.jar!/&quot; to &quot;/com/example/hello.jar&quot;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">getUrlMainPath</span><span class="params">(URL url)</span> <span class="keyword">throws</span> UnsupportedEncodingException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (url == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">		</span><br><span class="line">		<span class="comment">// 如果不使用URLDecoder解码的话，路径会出现中文乱码问题</span></span><br><span class="line">        String filePath = URLDecoder.decode(url.getFile(), <span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="comment">// if file is not the jar</span></span><br><span class="line">        <span class="keyword">int</span> pos = filePath.indexOf(<span class="string">&quot;!&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (-<span class="number">1</span> == pos)</span><br><span class="line">            <span class="keyword">return</span> filePath;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> filePath.substring(<span class="number">5</span>, pos);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">concat</span><span class="params">(Object... args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (args == <span class="keyword">null</span> || args.length == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        StringBuilder stringBuilder = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; args.length; i++)</span><br><span class="line">            stringBuilder.append(args[i]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> stringBuilder.toString();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>定义了这些辅助类之后，就可以去实现包扫描器了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimplePackageScanner</span> <span class="keyword">implements</span> <span class="title">PackageScanner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String packageName;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String packagePath;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> ClassLoader classLoader;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Logger logger;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SimplePackageScanner</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.classLoader = Thread.currentThread().getContextClassLoader();</span><br><span class="line">        <span class="keyword">this</span>.logger = LoggerFactory.getLogger(SimplePackageScanner.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Class&lt;?&gt;&gt; scan(String packageName) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.scan(packageName, <span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> List&lt;Class&lt;?&gt;&gt; scan(String packageName, ScannedClassHandler handler) &#123;</span><br><span class="line">        <span class="keyword">this</span>.initPackageNameAndPath(packageName);</span><br><span class="line">        <span class="keyword">if</span> (logger.isDebugEnabled())</span><br><span class="line">            logger.debug(<span class="string">&quot;Start scanning package: &#123;&#125; ....&quot;</span>, <span class="keyword">this</span>.packageName);</span><br><span class="line">        URL url = <span class="keyword">this</span>.getResource(<span class="keyword">this</span>.packagePath);</span><br><span class="line">        <span class="keyword">if</span> (url == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.parseUrlThenScan(url, handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initPackageNameAndPath</span><span class="params">(String packageName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.packageName = packageName;</span><br><span class="line">        <span class="keyword">this</span>.packagePath = PathUtils.packageToPath(packageName);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">&#125;	</span><br></pre></td></tr></table></figure>
<p>函数<code>getResource()</code>会根据包名来通过类加载器获得当前项目下的URL对象，如果这个URL为空则直接返回一个空的<code>ArrayList</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> URL <span class="title">getResource</span><span class="params">(String packagePath)</span> </span>&#123;</span><br><span class="line">    URL url = <span class="keyword">this</span>.classLoader.getResource(packagePath);</span><br><span class="line">    <span class="keyword">if</span> (url != <span class="keyword">null</span>)</span><br><span class="line">        logger.debug(<span class="string">&quot;Get resource: &#123;&#125; success!&quot;</span>, packagePath);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        logger.debug(<span class="string">&quot;Get resource: &#123;&#125; failed,end of scan.&quot;</span>, packagePath);</span><br><span class="line">    <span class="keyword">return</span> url;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>函数<code>parseUrlThenScan()</code>会解析URL对象并进行扫描，最终返回一个类列表。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">protected</span> List&lt;Class&lt;?&gt;&gt; parseUrlThenScan(URL url, ScannedClassHandler handler) &#123;</span><br><span class="line">      String urlPath = <span class="string">&quot;&quot;</span>;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">// 先提取出URL中的路径（不含协议名等信息）</span></span><br><span class="line">          urlPath = PathUtils.getUrlMainPath(url);</span><br><span class="line">      &#125; <span class="keyword">catch</span> (UnsupportedEncodingException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">          logger.debug(<span class="string">&quot;Get url path failed.&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="comment">// 判断URL的类型</span></span><br><span class="line">      ResourceType type = PathUtils.getResourceType(url);</span><br><span class="line">      List&lt;Class&lt;?&gt;&gt; classList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">          <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">              <span class="keyword">case</span> FILE:</span><br><span class="line">                  classList = <span class="keyword">this</span>.getClassListFromFile(urlPath, <span class="keyword">this</span>.packageName);</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">              <span class="keyword">case</span> JAR:</span><br><span class="line">                  classList = <span class="keyword">this</span>.getClassListFromJar(urlPath);</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">              <span class="keyword">default</span>:</span><br><span class="line">                  logger.debug(<span class="string">&quot;Unsupported file type.&quot;</span>);</span><br><span class="line">          &#125;</span><br><span class="line">      &#125; <span class="keyword">catch</span> (IOException | ClassNotFoundException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">          logger.debug(<span class="string">&quot;Get class list failed.&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行回调函数</span></span><br><span class="line">      <span class="keyword">this</span>.invokeCallback(classList, handler);</span><br><span class="line">      logger.debug(<span class="string">&quot;End of scan &lt;&#123;&#125;&gt;.&quot;</span>, urlPath);</span><br><span class="line">      <span class="keyword">return</span> classList;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>
<p>函数<code>getClassListFromFile()</code>会扫描路径下的所有class文件，并拼接包名生成Class对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> List&lt;Class&lt;?&gt;&gt; getClassListFromFile(String path, String packageName) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">    File file = <span class="keyword">new</span> File(path);</span><br><span class="line">    List&lt;Class&lt;?&gt;&gt; classList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    File[] listFiles = file.listFiles();</span><br><span class="line">    <span class="keyword">if</span> (listFiles != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (File f : listFiles) &#123;</span><br><span class="line">            <span class="keyword">if</span> (f.isDirectory()) &#123;</span><br><span class="line">	<span class="comment">// 如果是一个文件夹，则继续递归调用，注意传递的包名</span></span><br><span class="line">                List&lt;Class&lt;?&gt;&gt; list = getClassListFromFile(f.getAbsolutePath(),</span><br><span class="line">                        PathUtils.concat(packageName, <span class="string">&quot;.&quot;</span>, f.getName()));</span><br><span class="line">                classList.addAll(list);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (PathUtils.isClassFile(f.getName())) &#123;</span><br><span class="line">                <span class="comment">// 我们不添加名字带有$的class文件，这些都是JVM动态生成的</span></span><br><span class="line">                String className = PathUtils.trimSuffix(f.getName());</span><br><span class="line">                <span class="keyword">if</span> (-<span class="number">1</span> != className.lastIndexOf(<span class="string">&quot;$&quot;</span>))</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line">                String finalClassName = PathUtils.concat(packageName, <span class="string">&quot;.&quot;</span>, className);</span><br><span class="line">                classList.add(Class.forName(finalClassName));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> classList;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>函数<code>getClassListFromJar()</code>会扫描Jar中的class文件。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> List&lt;Class&lt;?&gt;&gt; getClassListFromJar(String jarPath) <span class="keyword">throws</span> IOException, ClassNotFoundException &#123;</span><br><span class="line">    <span class="keyword">if</span> (logger.isDebugEnabled())</span><br><span class="line">        logger.debug(<span class="string">&quot;Start scanning jar: &#123;&#125;&quot;</span>, jarPath);</span><br><span class="line"></span><br><span class="line">    JarInputStream jarInputStream = <span class="keyword">new</span> JarInputStream(<span class="keyword">new</span> FileInputStream(jarPath));</span><br><span class="line">    JarEntry jarEntry = jarInputStream.getNextJarEntry();</span><br><span class="line">    List&lt;Class&lt;?&gt;&gt; classList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (jarEntry != <span class="keyword">null</span>) &#123;</span><br><span class="line">        String name = jarEntry.getName();</span><br><span class="line">        <span class="keyword">if</span> (name.startsWith(<span class="keyword">this</span>.packageName) &amp;&amp; PathUtils.isClassFile(name))</span><br><span class="line">            classList.add(Class.forName(name));</span><br><span class="line">        jarEntry = jarInputStream.getNextJarEntry();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> classList;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>函数<code>invokeCallback()</code>遍历类对象列表，然后执行回调函数。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">invokeCallback</span><span class="params">(List&lt;Class&lt;?&gt;&gt; classList, ScannedClassHandler handler)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (classList != <span class="keyword">null</span> &amp;&amp; handler != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (Class&lt;?&gt; clazz : classList) &#123;</span><br><span class="line">            handler.execute(clazz);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>本节中实现的包扫描器源码地址：<a target="_blank" rel="noopener" href="https://gist.github.com/SylvanasSun/6ab31dcfd9670f29a46917decdba36d1">https://gist.github.com/SylvanasSun/6ab31dcfd9670f29a46917decdba36d1</a></p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/yuwanzi.io/tags/Java/" rel="tag"># Java</a>
              <a href="/yuwanzi.io/tags/2017/" rel="tag"># 2017</a>
              <a href="/yuwanzi.io/tags/%E5%90%8E%E7%AB%AF/" rel="tag"># 后端</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/yuwanzi.io/2017/10/08/2017-10-08-BrowserRenderOptimization/" rel="prev" title="浏览器性能优化-渲染性能">
                  <i class="fa fa-chevron-left"></i> 浏览器性能优化-渲染性能
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/yuwanzi.io/2017/10/29/2017-10-29-virtual_memory/" rel="next" title="虚拟内存的那点事儿">
                  虚拟内存的那点事儿 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>







<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>
</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">玉丸子</span>
</div>
  <div class="powered-by">Erstellt mit  <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

    </div>
  </footer>

  
  <script src="//cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
<script src="/yuwanzi.io/js/utils.js"></script><script src="/yuwanzi.io/js/motion.js"></script><script src="/yuwanzi.io/js/schemes/muse.js"></script><script src="/yuwanzi.io/js/next-boot.js"></script>

  






  





</body>
</html>
